﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.Reflection.Emit;


namespace Fast.Framework.Extensions
{

    /// <summary>
    /// 属性信息扩展类
    /// </summary>
    public static class PropertyInfoExtensions
    {

        /// <summary>
        /// 快速设置缓存
        /// </summary>
        private static readonly ConcurrentDictionary<string, Action<object, object>> fastSetCache = new ConcurrentDictionary<string, Action<object, object>>();

        /// <summary>
        /// 快速设置构建
        /// </summary>
        /// <param name="property">属性信息对象</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="Exception"></exception>
        public static Action<object, object> FastSetValueBuild(this PropertyInfo property)
        {
            if (property == null)
            {
                throw new ArgumentNullException(nameof(property));
            }
            if (!property.CanWrite)
            {
                throw new Exception($"属性名称:{property.Name} 不支持写入");
            }
            var key = $"{property.DeclaringType.GUID}_{property.Name}";
            return fastSetCache.GetOrAdd(key, k =>
            {
                var setMethod = property.SetMethod;
                var dynamicMethod = new DynamicMethod("FastSetValue", setMethod.ReturnType, new Type[] { typeof(object), typeof(object) }, property.DeclaringType, true);
                var il = dynamicMethod.GetILGenerator();

                if (!setMethod.IsStatic)
                {
                    il.Emit(OpCodes.Ldarg_0);
                }
                il.Emit(OpCodes.Ldarg_1);
                EmitConvert(il, property.PropertyType);
                if (!setMethod.IsStatic && !property.DeclaringType.IsValueType)
                {
                    il.Emit(OpCodes.Callvirt, setMethod);
                }
                else
                {
                    il.Emit(OpCodes.Call, setMethod);
                }
                il.Emit(OpCodes.Ret);
                return dynamicMethod.CreateDelegate(typeof(Action<object, object>)) as Action<object, object>;
            });
        }

        /// <summary>
        /// 快速设置构建
        /// </summary>
        /// <param name="propertys">属性信息集合</param>
        /// <returns></returns>
        public static Dictionary<string, Action<object, object>> FastSetValueBuild(this IEnumerable<PropertyInfo> propertys)
        {
            if (propertys == null)
            {
                throw new ArgumentNullException(nameof(propertys));
            }
            var keyValues = new Dictionary<string, Action<object, object>>();
            foreach (var property in propertys)
            {
                keyValues.Add(property.Name, property.FastSetValueBuild());
            }
            return keyValues;
        }

        /// <summary>
        /// Emit转换
        /// </summary>
        /// <param name="il">IL</param>
        /// <param name="type">类型</param>
        private static void EmitConvert(ILGenerator il, Type type)
        {
            if (type.IsValueType)
            {
                il.Emit(OpCodes.Unbox_Any, type);
            }
            else
            {
                il.Emit(OpCodes.Castclass, type);
            }
        }
    }
}
